package com.bac.rentmap.web.controller.cron;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import com.bac.rentmap.ad.batch.correction.CorrectionCalculator;

public class RandomCronController extends AbstractController {
    private static final Logger log = Logger
            .getLogger(RandomCronController.class.getName());
    private List<AccumulatedUrlProbability> urlProbabilities;
    private Random random = new Random();
    @SuppressWarnings("unchecked")
    private List<String> urlsWithLargerPariod = Collections.EMPTY_LIST;
    private CorrectionCalculator correctionCalculator;
    private Map<String, Number> urlPeriods;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        log.info("Executing cron");
        String url = getRandomUrl();
        log.info("Redirecting to " + url);
        request.getRequestDispatcher(url).forward(request, response);
        return null;
    }

    private String getRandomUrl() {
        double d = random.nextDouble();
        String previousUrl = null;
        mayBeEvaluateUrlProbabilities();
        for (AccumulatedUrlProbability accumulatedUrlProbability : urlProbabilities) {
            if (d >= accumulatedUrlProbability.getProbability()) {
                previousUrl = accumulatedUrlProbability.getUrl();
            } else {
                return previousUrl;
            }
        }
        return urlProbabilities.get(urlProbabilities.size() - 1).getUrl();
    }

    private static class AccumulatedUrlProbability {
        private double probability;
        private String url;

        private AccumulatedUrlProbability(double probability, String url) {
            this.probability = probability;
            this.url = url;
        }

        public double getProbability() {
            return probability;
        }

        public String getUrl() {
            return url;
        }

        @Override
        public String toString() {
            return probability + ": " + url;
        }
    }

    @Required
    public void setUrlPeriods(Map<String, Number> urlPeriods) {
    	this.urlPeriods = urlPeriods;
    }
    
    private void mayBeEvaluateUrlProbabilities(){
    	if(urlProbabilities != null)
    		return;
        Map<String, Double> urlFrequency = new HashMap<String, Double>();
        for (Map.Entry<String, Number> entry : urlPeriods.entrySet()) {
            double frequency = 1 / entry.getValue().doubleValue();
            double correction;
            if (urlsWithLargerPariod.contains(entry.getKey())) {
                correction = 5;
            }
            else{
                correction = correctionCalculator.calculate(entry.getKey());
            }

            urlFrequency.put(entry.getKey(), frequency * correction);
        }
        double total = 0;
        for (Double frequency : urlFrequency.values()) {
            total += frequency;
        }
        double p = 0;
        urlProbabilities = new ArrayList<AccumulatedUrlProbability>();
        for (Map.Entry<String, Double> entry : urlFrequency.entrySet()) {
            urlProbabilities.add(new AccumulatedUrlProbability(p / total, entry
                    .getKey()));
            p += entry.getValue().doubleValue();
        }
        log.config("URL probabilities: " + urlProbabilities);    	
    }

    @Required
    public void setUrlsWithLargerPariod(List<String> urlsWithLargerPariod) {
        this.urlsWithLargerPariod = urlsWithLargerPariod;
    }

    @Required
    public void setCorrectionCalculator(CorrectionCalculator correctionCalculator) {
        this.correctionCalculator = correctionCalculator;
    }
    
    public void resetCorrectionCache(){
        urlProbabilities = null;
    }
}
